package hawk.example.bookstore.repository.impl;

import static glz.hawkframework.support.ArgumentSupport.*;

import glz.hawkframework.dao.base.process.InsertProcessor;
import glz.hawkframework.dao.base.process.UpdateProcessor;
import glz.hawkframework.sql.condition.Condition;
import glz.hawkframework.sql.util.QueryWrapper;

import hawk.example.bookstore.mapper.BookInventoryMapper;
import hawk.example.bookstore.po.BookInventoryPo;
import hawk.example.bookstore.repository.BookInventoryRepository;
import hawk.example.bookstore.sql.provider.BookInventorySqlProvider;
import hawk.example.bookstore.update.BookInventoryColumnUpdate;
import hawk.example.bookstore.update.BookInventoryUpdate;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import javax.annotation.Nullable;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class BookInventoryRepositoryImpl implements BookInventoryRepository {
    private final BookInventoryMapper bookInventoryMapper;
    private final BookInventorySqlProvider bookInventorySqlProvider;
    @Autowired(required = false)
    private InsertProcessor insertProcessor;
    @Autowired(required = false)
    private UpdateProcessor updateProcessor;

    public BookInventoryRepositoryImpl(BookInventoryMapper bookInventoryMapper, BookInventorySqlProvider bookInventorySqlProvider) {
        this.bookInventoryMapper = argNotNull(bookInventoryMapper, "bookInventoryMapper");
        this.bookInventorySqlProvider = argNotNull(bookInventorySqlProvider, "bookInventorySqlProvider");
    }

    @Override
    public void insert(BookInventoryPo bookInventoryPo) {
        argNotNull(bookInventoryPo, "bookInventoryPo");
        if (insertProcessor != null) {
            insertProcessor.beforeInsert(bookInventoryPo);
        }
        if (bookInventoryMapper.insert(bookInventorySqlProvider.insert(bookInventoryPo)) != 1) {
            throw new IllegalStateException("Failed to insert bookInventoryPo.");
        }
        if (insertProcessor != null) {
            insertProcessor.afterInsert(bookInventoryPo);
        }
    }

    @Override
    public void insertSelective(BookInventoryPo bookInventoryPo) {
        argNotNull(bookInventoryPo, "bookInventoryPo");
        if (insertProcessor != null) {
            insertProcessor.beforeInsert(bookInventoryPo);
        }
        if (bookInventoryMapper.insertSelective(bookInventorySqlProvider.insertSelective(bookInventoryPo)) != 1) {
            throw new IllegalStateException("Failed to insert bookInventoryPo.");
        }
        if (insertProcessor != null) {
            insertProcessor.afterInsert(bookInventoryPo);
        }
    }

    @Override
    public void insertMultiple(List<BookInventoryPo> bookInventoryPos) {
        final int defaultChunkSize = 200;
        insertMultiple(bookInventoryPos, defaultChunkSize);
    }

    @Override
    public void insertMultiple(List<BookInventoryPo> bookInventoryPos, int chunkSize) {
        argNotEmpty(bookInventoryPos, "bookInventoryPos");
        argument(chunkSize, c -> c > 0, c -> "The parameter['chunkSize'] must be greater than 0.");
        for (int i = 0; i < bookInventoryPos.size(); i += chunkSize) {
            List<BookInventoryPo> chunk = bookInventoryPos.subList(i, Math.min(i + chunkSize, bookInventoryPos.size()));
            if (insertProcessor != null) {
                insertProcessor.beforeInsert(chunk);
            }
            if (bookInventoryMapper.insertMultiple(bookInventorySqlProvider.insertMultiple(chunk)) != chunk.size()) {
                throw new IllegalStateException("Failed to insert bookInventoryPos.");
            }
            if (insertProcessor != null) {
                insertProcessor.afterInsert(chunk);
            }
        }
    }

    @Override
    public void deleteByPrimaryKey(BigInteger bookInventoryId, Long recordVersion) {
        argNotNull(bookInventoryId, "bookInventoryId");
        argNotNull(recordVersion, "recordVersion");
        if (bookInventoryMapper.deleteGeneral(bookInventorySqlProvider.deleteByPrimaryKey(bookInventoryId, recordVersion)) != 1) {
            throw new IllegalStateException("Failed to delete bookInventoryPo.");
        }
    }

    @Override
    public void updateByPrimaryKey(BookInventoryUpdate bookInventoryUpdate, BigInteger bookInventoryId, Long recordVersion) {
        argNotNull(bookInventoryUpdate, "bookInventoryUpdate");
        argNotNull(bookInventoryId, "bookInventoryId");
        argNotNull(recordVersion, "recordVersion");
        HashMap<String, Object> params = null;
        if (updateProcessor != null) {
            params = new HashMap<>();
            params.put(UpdateProcessor.UPDATE_BY_FIELD_OBJECT, bookInventoryUpdate);
            params.put(UpdateProcessor.RECORD_VERSION, recordVersion);
            params.put(UpdateProcessor.RECORD_ID, bookInventoryId);
            updateProcessor.beforeUpdate(params);
        }
        if (bookInventoryMapper.update(bookInventorySqlProvider.updateByPrimaryKey(bookInventoryUpdate, bookInventoryId, recordVersion)) != 1) {
            throw new IllegalStateException("Failed to update bookInventoryPo.");
        }
        if (updateProcessor != null) {
            updateProcessor.afterUpdate(params);
        }
    }

    @Override
    public Optional<BookInventoryPo> getByPrimaryKey(BigInteger bookInventoryId) {
        argNotNull(bookInventoryId, "bookInventoryId");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectByPrimaryKey(bookInventoryId));
    }

    /**
     * Throws an exception if found no required record by the primary key.
     */
    @Override
    public BookInventoryPo loadByPrimaryKey(BigInteger bookInventoryId) {
        argNotNull(bookInventoryId, "bookInventoryId");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectByPrimaryKey(bookInventoryId)).orElseThrow(RuntimeException::new);
    }

    /**
     * Throws the supplied exception if found no required record by the primary key.
     */
    @Override
    public <E extends Throwable> BookInventoryPo loadByPrimaryKey(BigInteger bookInventoryId, Supplier<? extends E> exceptionSupplier) throws E {
        argNotNull(bookInventoryId, "bookInventoryId");
        argNotNull(exceptionSupplier, "exceptionSupplier");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectByPrimaryKey(bookInventoryId)).orElseThrow(exceptionSupplier);
    }

    @Override
    public boolean existByPrimaryKey(BigInteger bookInventoryId) {
        argNotNull(bookInventoryId, "bookInventoryId");
        return bookInventoryMapper.count(bookInventorySqlProvider.countByPrimaryKey(bookInventoryId)) == 1;
    }

    /**
     * Throws an exception if the required record doesn't exist
     */
    @Override
    public void assertExistByPrimaryKey(BigInteger bookInventoryId) {
        argNotNull(bookInventoryId, "bookInventoryId");
        if (bookInventoryMapper.count(bookInventorySqlProvider.countByPrimaryKey(bookInventoryId)) != 1) {
            throw new RuntimeException("Record not exist");
        }
    }

    /**
     * Throws the supplied exception if the required record doesn't exist
     */
    @Override
    public <E extends Throwable> void assertExistByPrimaryKey(BigInteger bookInventoryId, Supplier<? extends E> exceptionSupplier) throws E {
        argNotNull(bookInventoryId, "bookInventoryId");
        argNotNull(exceptionSupplier, "exceptionSupplier");
        if (bookInventoryMapper.count(bookInventorySqlProvider.countByPrimaryKey(bookInventoryId)) != 1) {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public Optional<BookInventoryPo> queryOne(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), q -> !q.isCount(), q -> "The count field in the queryWrapper should be set to false.");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper));
    }

    @Override
    public List<BookInventoryPo> queryMany(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), q -> !q.isCount(), q -> "The count field in the queryWrapper should be set to false.");
        return bookInventoryMapper.selectMany(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper));
    }

    /**
     * Throws an exception if found no required record.
     */
    @Override
    public BookInventoryPo loadOne(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), q -> !q.isCount(), q -> "The count field in the queryWrapper should be set to false.");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)).orElseThrow(RuntimeException::new);
    }

    /**
     * Throws the supplied exception if found no required record.
     */
    @Override
    public <E extends Throwable> BookInventoryPo loadOne(QueryWrapper queryWrapper, Supplier<? extends E> exceptionSupplier) throws E {
        argument(argNotNull(queryWrapper, "queryWrapper"), q -> !q.isCount(), q -> "The count field in the queryWrapper should be set to false.");
        argNotNull(exceptionSupplier, "exceptionSupplier");
        return bookInventoryMapper.selectOne(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)).orElseThrow(exceptionSupplier);
    }

    @Override
    public long count(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        return bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper));
    }

    @Override
    public boolean exist(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        return bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) > 0;
    }

    /**
     * Throws an exception if found no required record.
     */
    @Override
    public void assertExist(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) <= 0) {
            throw new RuntimeException();
        }
    }

    /**
     * Throws the supplied exception if found no required record.
     */
    @Override
    public <E extends Throwable> void assertExist(QueryWrapper queryWrapper, Supplier<? extends E> exceptionSupplier) throws E {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        argNotNull(exceptionSupplier, "exceptionSupplier");
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) <= 0) {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public boolean existOne(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        return bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) == 1;
    }

    /**
     * Throws an exception if found no required record or found more than one record.
     */
    @Override
    public void assertExistOne(QueryWrapper queryWrapper) {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) == 0) {
            throw new RuntimeException();
        }
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) > 1) {
            throw new RuntimeException();
        }
    }

    /**
     * Throws the supplied exception if found no required record or found more than one record.
     */
    @Override
    public <E extends Throwable> void assertExistOne(QueryWrapper queryWrapper, Supplier<? extends E> exceptionSupplier) throws E {
        argument(argNotNull(queryWrapper, "queryWrapper"), QueryWrapper::isCount, q -> "The count field in the queryWrapper should be set to true.");
        argNotNull(exceptionSupplier, "exceptionSupplier");
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) == 0) {
            throw exceptionSupplier.get();
        }
        if (bookInventoryMapper.count(bookInventorySqlProvider.selectOrCountDynamic(queryWrapper)) > 1) {
            throw exceptionSupplier.get();
        }
    }

    @Override
    public long delete(@Nullable Condition condition) {
        return bookInventoryMapper.deleteGeneral(bookInventorySqlProvider.deleteDynamic(condition));
    }

    @Override
    public long update(BookInventoryColumnUpdate bookInventoryColumnUpdate, @Nullable Condition condition) {
        argNotNull(bookInventoryColumnUpdate,"bookInventoryColumnUpdate");
        HashMap<String, Object> params = null;
        if (updateProcessor != null) {
            params = new HashMap<>();
            params.put(UpdateProcessor.UPDATE_BY_COLUMN_OBJECT, bookInventoryColumnUpdate);
            updateProcessor.beforeUpdate(params);
        }
        long affectedRowCount =  bookInventoryMapper.updateGeneral(bookInventorySqlProvider.updateDynamic(bookInventoryColumnUpdate, condition));
        if (updateProcessor != null) {
            updateProcessor.afterUpdate(params);
        }
        return affectedRowCount;
    }
}
